import { RepoModel } from '@models/repo';
import EmitTask from '@socket/utils/emitTask';
import { constants, promises } from 'fs';
import { join } from 'path';
import { Socket } from 'socket.io';

import config from '../config';
import runCommand from './utils/runCommand';

/**
 * 拉取/更新代码
 * @param socket Socket 实例
 * @param appId 应用ID
 * @param repoId 仓库ID
 */
export async function updateCode(socket: Socket, appId: string, repoId: string): Promise<void> {
  // 创建 EmitTask 实例，用于发送任务状态
  const emitTask = new EmitTask(socket, 'update-code');

  // 开始任务
  emitTask.start();
  // 应用目录
  const appDir = join(config.appPath, appId);
  emitTask.info(`应用存储路径: ${appDir}`);
  // 确保应用目录存在,如果不存在则创建
  try {
    await promises.access(config.appPath, constants.F_OK);
  } catch (err) {
    try {
      await promises.mkdir(config.appPath, { recursive: true });
    } catch (mkdirErr) {
      emitTask.error(`创建应用数据存储路径失败: ${String(mkdirErr)}`);
      emitTask.errorEnd();
      return;
    }
  }

  // 查询仓库信息
  const repo = await RepoModel.findOne({ _id: repoId });
  if (!repo) {
    emitTask.error(`仓库${repoId}不存在`);
    emitTask.errorEnd();
    return;
  }
  emitTask.info(`仓库 ${repoId} 存在，开始拉取代码。`);

  // 仓库目录
  const repoDir = join(appDir, repo.name);
  // 仓库目录不存在则克隆仓库，仓库目录存在则拉取最新代码
  try {
    await promises.access(repoDir, constants.F_OK);
    emitTask.info(`仓库目录已存在, 开始拉取最新代码。`);
    emitTask.info(`执行命令: git pull`);
    try {
      await runCommand(emitTask, 'git', ['pull'], {
        cwd: repoDir,
        shell: true,
      });
      emitTask.successEnd();
      return;
    } catch (err) {
      emitTask.error(String(err));
      emitTask.errorEnd();
      return;
    }
  } catch (err) {
    emitTask.info(`仓库目录不存在，开始 clone 仓库。`);
    emitTask.info(`执行命令: git clone ${repo.git} -b ${repo.branch} ${repo.name}`);
    try {
      await runCommand(emitTask, 'git', ['clone', repo.git, '-b', repo.branch, repo.name], {
        cwd: appDir,
        shell: true,
      });
      emitTask.successEnd();
      return;
    } catch (err) {
      emitTask.error(String(err));
      emitTask.errorEnd();
      return;
    }
  }
}
